package com.myproject.mall.goods.service.impl;

import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.myproject.mall.common.constants.RedisConstants;
import com.myproject.mall.domain.entity.Goods;
import com.myproject.mall.domain.holder.UserHolder;
import com.myproject.mall.domain.vo.GoodsVO;
import com.myproject.mall.goods.mapper.GoodsMapper;
import com.myproject.mall.goods.service.IRecommendService;
import com.myproject.mall.goods.util.RecommendUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class RecommendServiceImpl implements IRecommendService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private RecommendUtil recommendUtil;

    private final int RECOMMEND_SIZE = 15;

    @Override
    public List<GoodsVO> recommend(String userId) {
        String key = RedisConstants.USER_HISTORY_PREFIX + userId;
        Set<String> viewHistory = redisTemplate.opsForZSet().reverseRange(key, 0, 99);
        List<Long> historyIds = Optional.ofNullable(viewHistory)
                .filter(hs -> !hs.isEmpty())
                .map(hs -> hs.stream().map(Long::parseLong).toList())
                .orElse(Collections.emptyList());

        List<GoodsVO> recommendList = new ArrayList<>(RECOMMEND_SIZE);

        // 获取当前时间作为随机因子
        long currentTime = System.currentTimeMillis();
        Random random = new Random(currentTime);

        // 如果有浏览历史，基于偏好推荐
        if (!historyIds.isEmpty()) {
            // 获取用户浏览过的商品信息
            List<Goods> historyGoods = goodsMapper.selectBatchIds(historyIds);

            // 统计用户偏好
            Map<Integer, Integer> categoryPreference = new HashMap<>();
            Map<Long, Integer> brandPreference = new HashMap<>();
            Map<Long, Integer> shopPreference = new HashMap<>();

            // 根据浏览时间远近设置不同的权重
            for (int i = 0; i < historyIds.size(); i++) {
                Long goodsId = historyIds.get(i);
                Goods goods = historyGoods.stream()
                        .filter(g -> g.getId().equals(goodsId))
                        .findFirst()
                        .orElse(null);

                if (goods != null) {
                    // 最近浏览的商品权重更高
                    int weight = Math.max(1, 10 - i/2); // 越靠前权重越高

                    // 统计品类偏好
                    if (goods.getCategoryId() != null) {
                        categoryPreference.put(goods.getCategoryId(),
                                categoryPreference.getOrDefault(goods.getCategoryId(), 0) + weight);
                    }

                    // 统计品牌偏好
                    if (goods.getBrandId() != null) {
                        brandPreference.put(goods.getBrandId(),
                                brandPreference.getOrDefault(goods.getBrandId(), 0) + weight);
                    }

                    // 统计店铺偏好
                    if (goods.getShopId() != null) {
                        shopPreference.put(goods.getShopId(),
                                shopPreference.getOrDefault(goods.getShopId(), 0) + weight);
                    }
                }
            }

            // 基于品类偏好推荐（添加随机性）
            if (!categoryPreference.isEmpty()) {
                List<Map.Entry<Integer, Integer>> sortedCategories = categoryPreference.entrySet()
                        .stream()
                        .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed())
                        .collect(Collectors.toList());

                // 添加随机性，打乱顺序但保持权重影响
                shuffleWithWeight(sortedCategories, random);

                int categoryRecommendCount = Math.min(5, RECOMMEND_SIZE/3); // 最多推荐1/3

                for (Map.Entry<Integer, Integer> entry : sortedCategories) {
                    if (recommendList.size() >= categoryRecommendCount) break;

                    // 查询该品类下的商品（排除已浏览过的）
                    List<GoodsVO> categoryGoods = goodsMapper.selectJoinList(GoodsVO.class,
                            new MPJLambdaWrapper<Goods>()
                                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures)
                                    .eq(Goods::getCategoryId, entry.getKey())
                                    .notIn(Goods::getId, historyIds)
                                    .last("LIMIT 20") // 增加候选数量
                    );

                    // 随机选择商品
                    if (!categoryGoods.isEmpty()) {
                        Collections.shuffle(categoryGoods, new Random(currentTime + entry.getKey()));
                        int addedCount = 0;
                        for (GoodsVO goods : categoryGoods) {
                            if (recommendList.size() >= categoryRecommendCount || addedCount >= 2) break;
                            if (recommendList.stream().noneMatch(g -> g.getId().equals(goods.getId()))) {
                                recommendList.add(goods);
                                addedCount++;
                            }
                        }
                    }
                }
            }

            // 基于品牌偏好推荐（添加随机性）
            if (!brandPreference.isEmpty() && recommendList.size() < RECOMMEND_SIZE * 0.3) {
                List<Map.Entry<Long, Integer>> sortedBrands = brandPreference.entrySet()
                        .stream()
                        .sorted(Map.Entry.<Long, Integer>comparingByValue().reversed())
                        .collect(Collectors.toList());

                // 添加随机性
                shuffleWithWeight(sortedBrands, random);

                int brandRecommendCount = Math.min((int)(RECOMMEND_SIZE * 0.3), 5);

                for (Map.Entry<Long, Integer> entry : sortedBrands) {
                    if (recommendList.size() >= brandRecommendCount) break;

                    // 查询该品牌下的商品（排除已浏览过的）
                    List<GoodsVO> brandGoods = goodsMapper.selectJoinList(GoodsVO.class,
                            new MPJLambdaWrapper<Goods>()
                                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures)
                                    .eq(Goods::getBrandId, entry.getKey())
                                    .notIn(Goods::getId, historyIds)
                                    .last("LIMIT 15")
                    );

                    // 随机选择商品
                    if (!brandGoods.isEmpty()) {
                        Collections.shuffle(brandGoods, new Random(currentTime + entry.getKey() * 2));
                        int addedCount = 0;
                        for (GoodsVO goods : brandGoods) {
                            if (recommendList.size() >= brandRecommendCount || addedCount >= 2) break;
                            if (recommendList.stream().noneMatch(g -> g.getId().equals(goods.getId()))) {
                                recommendList.add(goods);
                                addedCount++;
                            }
                        }
                    }
                }
            }

            // 基于店铺偏好推荐（添加随机性）
            if (!shopPreference.isEmpty() && recommendList.size() < RECOMMEND_SIZE * 0.3) {
                List<Map.Entry<Long, Integer>> sortedShops = shopPreference.entrySet()
                        .stream()
                        .sorted(Map.Entry.<Long, Integer>comparingByValue().reversed())
                        .collect(Collectors.toList());

                // 添加随机性
                shuffleWithWeight(sortedShops, random);

                int shopRecommendCount = Math.min((int)(RECOMMEND_SIZE * 0.3), 5);

                for (Map.Entry<Long, Integer> entry : sortedShops) {
                    if (recommendList.size() >= shopRecommendCount) break;

                    // 查询该店铺下的商品（排除已浏览过的）
                    List<GoodsVO> shopGoods = goodsMapper.selectJoinList(GoodsVO.class,
                            new MPJLambdaWrapper<Goods>()
                                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures)
                                    .eq(Goods::getShopId, entry.getKey())
                                    .notIn(Goods::getId, historyIds)
                                    .last("LIMIT 15")
                    );

                    // 随机选择商品
                    if (!shopGoods.isEmpty()) {
                        Collections.shuffle(shopGoods, new Random(currentTime + entry.getKey() * 3));
                        int addedCount = 0;
                        for (GoodsVO goods : shopGoods) {
                            if (recommendList.size() >= shopRecommendCount || addedCount >= 2) break;
                            if (recommendList.stream().noneMatch(g -> g.getId().equals(goods.getId()))) {
                                recommendList.add(goods);
                                addedCount++;
                            }
                        }
                    }
                }
            }
        }

        // 用热门商品补充推荐
        if (recommendList.size() < RECOMMEND_SIZE * 0.6) {
            int remaining = (int) (RECOMMEND_SIZE * 0.6 - recommendList.size());

            // 获取热门商品
            Set<String> hotGoodsKeys = redisTemplate.opsForZSet()
                    .reverseRange(RedisConstants.GOODS_HOT_KEY, 0, 100);

            if (hotGoodsKeys != null && !hotGoodsKeys.isEmpty()) {
                List<Long> hotGoodsIds = hotGoodsKeys.stream()
                        .map(Long::valueOf)
                        .filter(id -> historyIds.stream().noneMatch(historyId -> historyId.equals(id))) // 排除已浏览
                        .filter(id -> recommendList.stream().noneMatch(g -> g.getId().equals(id))) // 排除已推荐
                        .collect(Collectors.toList());

                // 随机选择热门商品
                if (!hotGoodsIds.isEmpty()) {
                    Collections.shuffle(hotGoodsIds, new Random(currentTime * 4));
                    hotGoodsIds = hotGoodsIds.stream().limit(remaining * 2).collect(Collectors.toList());

                    List<GoodsVO> hotGoods = goodsMapper.selectJoinList(GoodsVO.class,
                            new MPJLambdaWrapper<Goods>()
                                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures)
                                    .in(Goods::getId, hotGoodsIds)
                                    .last("LIMIT " + remaining)
                    );

                    recommendList.addAll(hotGoods);
                }
            }
        }

        // 用随机商品补充到目标数量
        if (recommendList.size() < RECOMMEND_SIZE) {
            int remaining = RECOMMEND_SIZE - recommendList.size();

            // 构建排除已浏览和已推荐商品的查询
            MPJLambdaWrapper<Goods> randomWrapper = new MPJLambdaWrapper<Goods>()
                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures);

            if (!historyIds.isEmpty()) {
                randomWrapper.notIn(Goods::getId, historyIds);
            }

            if (!recommendList.isEmpty()) {
                List<Long> recommendedIds = recommendList.stream()
                        .map(GoodsVO::getId)
                        .toList();
                randomWrapper.notIn(Goods::getId, recommendedIds);
            }

            // 根据时间生成不同的随机种子
            randomWrapper.last("ORDER BY RAND() LIMIT " + (remaining * 3));

            List<GoodsVO> randomGoods = goodsMapper.selectJoinList(GoodsVO.class, randomWrapper);

            // 再次随机化并选择
            if (!randomGoods.isEmpty()) {
                Collections.shuffle(randomGoods, new Random(currentTime * 5));
                int addedCount = 0;
                for (GoodsVO goods : randomGoods) {
                    if (addedCount >= remaining) break;
                    if (recommendList.stream().noneMatch(g -> g.getId().equals(goods.getId()))) {
                        recommendList.add(goods);
                        addedCount++;
                    }
                }
            }
        }

        // 如果还是不够数量，放宽限制条件
        if (recommendList.size() < RECOMMEND_SIZE) {
            int remaining = RECOMMEND_SIZE - recommendList.size();

            MPJLambdaWrapper<Goods> fillWrapper = new MPJLambdaWrapper<Goods>()
                    .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures)
                    .notIn(!historyIds.isEmpty(), Goods::getId, historyIds)
                    .last("ORDER BY RAND() LIMIT " + remaining);

            List<GoodsVO> fillGoods = goodsMapper.selectJoinList(GoodsVO.class, fillWrapper);
            recommendList.addAll(fillGoods);
        }

        // 最终去重并确保数量正确
        return recommendList.stream()
                .distinct()
                .limit(RECOMMEND_SIZE)
                .collect(Collectors.toList());
    }

    @Override
    public void addViewHistory(Long view) {
        String userId = UserHolder.getUser().getId();
        String key = RedisConstants.USER_HISTORY_PREFIX + userId;
        redisTemplate.opsForZSet().add(key, view.toString(), System.currentTimeMillis());
        redisTemplate.expire(key, RedisConstants.USER_HISTORY_EXPIRE, TimeUnit.MINUTES);
    }

    @Override
    public List<GoodsVO> getViewHistory() {
        String userId = UserHolder.getUser().getId();
        String key = RedisConstants.USER_HISTORY_PREFIX + userId;
        Set<String> viewHistory = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
        if(viewHistory == null || viewHistory.isEmpty()) return Collections.emptyList();

        Set<Long> ids = viewHistory.stream().map(Long::parseLong).collect(Collectors.toSet());

        return goodsMapper.selectJoinList(GoodsVO.class, new MPJLambdaWrapper<Goods>()
                .in(Goods::getId, ids)
                .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures));
    }

    private void makeHot(Long goodsId, int add) {
        String hotkey = RedisConstants.GOODS_HOT_KEY;
//        增加商品热度
        redisTemplate.opsForZSet().incrementScore(hotkey, goodsId.toString(), add);
        redisTemplate.expire(hotkey, RedisConstants.GOODS_HOT_EXPIRE, TimeUnit.MINUTES);
    }

    @Override
    public List<GoodsVO> getHot(int time, int size) {
        String hotkey = RedisConstants.GOODS_HOT_KEY;
//        在距今time天前，获取榜前size个
        Set<String> hotGoods = redisTemplate.opsForZSet().reverseRangeByScore(hotkey, System.currentTimeMillis() - (long) time * 24 * 60 * 60 * 1000, System.currentTimeMillis(), 0, size - 1);

        if(hotGoods == null || hotGoods.isEmpty()) return Collections.emptyList();

        List<Long> goodsIds = hotGoods.stream().map(Long::valueOf).toList();

        return goodsMapper.selectJoinList(GoodsVO.class, new MPJLambdaWrapper<Goods>()
                .in(Goods::getId, goodsIds)
                .select(Goods::getId, Goods::getTitle, Goods::getPrice, Goods::getPictures));
    }

    private <T> void shuffleWithWeight(List<Map.Entry<T, Integer>> list, Random random) {
        // 创建一个副本进行操作
        List<Map.Entry<T, Integer>> temp = new ArrayList<>(list);
        list.clear();

        while (!temp.isEmpty()) {
            // 计算总权重
            int totalWeight = temp.stream().mapToInt(Map.Entry::getValue).sum();

            // 根据权重随机选择一个元素
            int randomValue = random.nextInt(totalWeight);
            int currentWeight = 0;
            Map.Entry<T, Integer> selected = null;
            int selectedIndex = 0;

            for (int i = 0; i < temp.size(); i++) {
                currentWeight += temp.get(i).getValue();
                if (randomValue < currentWeight) {
                    selected = temp.get(i);
                    selectedIndex = i;
                    break;
                }
            }

            if (selected != null) {
                list.add(selected);
                temp.remove(selectedIndex);
            } else if (!temp.isEmpty()) {
                // fallback 机制
                list.add(temp.remove(0));
            }
        }
    }
}
